Chapter 2: Python Bascis II
From book Python Programming (Problem solving, Packages and Libraries) published by McGraw Hill Education (India) Private limited. By:

  • Anurag Gupta
  • G. P. Biswas

Note the following:-

  1. This html document is meant as an accompaniment to Chapter 2 Python Basics II .
  2. The document contains scripts executed on IDLE as well as on Jupyter notebook.
  3. The scripts executed on Jupyter can be directly copied and run into a Jupyter notebook or some other IDE (Like Pycharm or Eclipse with PyDev or Visual studio).
  4. However the scripts on IDLE also contain the >>> symbol and therefore cannot be directly executed. If you want to execute them on IDLE or Jupyter, you need to manually remove the >>> symbol.
  5. Wherever needed some background material from the book is also included to help you better understand the scripts
  6. The topic numbers given on each paragraph match the topic numbers of the book, so you can easily identify the topics and corresponding scripts.
  7. At some places, to improve readability, page numbers of the book are indicated in green font like:- See Page 181 of the book
  8. In some of the scripts, the file paths give are that of the author's computer. You need to replace them with file paths of your own computer.
  9. This document was first created as a Jupyter Notebook as combination of Markdown and code cells (extension .ipynb) and then downloaded as html. If someone wants to "modify" or "extend' this document, you may ask for the original .ipynb file by sending me an e-mail at:- 999.anuraggupta@gmail.com

2.2.1. Numbers (including integers, floating points, complex and bool)
Following script on IDLE shows the use of numeric data type:-

# ---ON IDLE---  
>>> myNumb = 24 # Now the variable myNumb refers to a number ie 24
>>> myNumb            # You can check that myNumb actually refers to 24
24
>>> myTotal = 23 + 24# Two numeric literals 23 & 24 added and assigned to myTotal
>>> myTotal
47

In Python 2.x, you can find the maximum integer permissible by using the following command:-

# ---ON IDLE---  
>>>import sys
>>> sys.maxint

2.2.2. complex (complex numbers) In Python, a complex number has the following characteristics:-

  • A complex number consists of an “ordered pair” of numbers of type x + yj, where x and y may be int or float.
  • Here, x represents the “real” part of the number while “y” represents the “imaginary” part of the number.
  • The term “ordered pair” means that the order of the number x and y matter. So x + yj is different from y + xj.

Python provides methods and functions to convert to and from complex numbers. The following example clarifes the concepts regarding complex numbers in Python:-

# ---ON IDLE---  
>>>2j  #This is a complex number with only imaginary part
2j
>>>1 + 2j  #Complex number with real and imag part
(1+2j)
>>> complex(2,3)    #Function complex(x,y) creates a complex number
(2+3j)
>>> z = 2 + 3j
>>> z.real  #The z.real property of complex numbers gets the real part
2.0
>>> z.imag  #The z.imag property gets imag part 
3.0
>>> z.conjugate()   #The conjugate() method gets conjugate
(2-3j)
>>> abs(2 + 3j)# absolute is ((x**2 + y**2)**1/2)
3.6055512754639896

2.2.4. Sequence and other containers (non-sequenced containers)
1. Strings
Some important characteristics of string data types are as follows:

  • Strings in Python are a “sequence of characters enclosed by quotation marks”. You can use pairs of either single or double quotes to mark the beginning and end of a string.
  • Since a string is a sequence, you can use an “index” to access its individual characters.
  • All index in Python start from 0. This means, the first character in a string will have index 0. If there are n characters in a string, then the index of the last item will be n-1.
  • You can use the slice operator “[m : n ]” with indexes m and n.
  • You can use the plus ( + ) operator for “concatenation” of two strings.
  • You can use the asterisk ( * ) operator to “multiply” a string with a positive integer. Here, “multiply” means “repetition”.

These concepts will be clear from the following example code on IDLE:

# ---ON IDLE---  
>>>myStr = 'Hello World!'
>>> str(myStr)  # Prints entire string
'Hello World!'
>>> myStr[0]    # Prints the character at index 0 which is the 1st character
'H'
>>> myStr[1:5]  # Prints characters from index 1 (2nd character) to index 4 (5th character) total 5-1-> 4 characters
'ello'
>>> myStr + 'From me'# Concatenates the two strings
'Hello World!From me'
>>> myStr * 3# myStr is concatenated 3 times
'Hello World!Hello World!Hello World!'
>>>

2. Lists
A list in Python is a sequenced container.
What does ‘container’ mean? A container in Python is an object which can contain other objects.
Hence, a list can contain any valid python object, such as strings, numbers or even other lists.
What does sequence mean? In a sequence in Python, each item is identified by an index, which starts from 0. Hence, if a list in Python has n items, then the sequence of first item is 0 and the sequence of the nth item is n-1.
A list contains items separated by commas and enclosed within square brackets ([ ]).
As an example, create a list of animals that are kept as pets and call this list pets.
Note that all the pet animal names are strings, and therefore, must be enclosed in single or double quotes.

# ---ON IDLE---  
>>> pets = ['cat', 'dog', 'fish', 'rabbit', 'parrot', 'snake']
>>> pets
['cat', 'dog', 'fish', 'rabbit', 'parrot', 'snake']
>>> pets[0]
'cat'
>>> pets[1:5]
['dog', 'fish', 'rabbit', 'parrot']
>>>

3. Tuples
Some important aspects of the tuple data type are as follows:

  • Tuples are also “sequences” and are very similar to lists.
  • A tuple has a number items/ objects/ values separated by commas.
  • However, while lists are enclosed within “square brackets”, tuples are enclosed within “parentheses”.
  • The elements of a list can be changed. So, lists are “mutable”. However, the elements of a tuple cannot be changed. So, tuples are “immutable”.
  • You can think of a tuple as a “read-only” list.

The following examples on IDLE clarify the concepts given:

# ---ON IDLE---  
>>> myT = (1, 2, 3, 4, 5, 6)
>>> myT
(1, 2, 3, 4, 5, 6)
>>> myT[0]
1
>>> myT[3:5]
(4, 5)
>>> myT
(1, 2, 3, 4, 5, 6)

Note:- Tuples can also be created by comma-separated items or objects without parenthesis. For example:-

# ---ON IDLE---  
>>> myTup = 1, 'two',3,'four'# Comma-separated items without parenthesis create tuple
>>> myTup
(1, 'two', 3, 'four')

Consider the following code. What is the difference between line 1 and line 2?

# ---ON IDLE---  
>>> one = 1
>>> two = 2,
>>> type(one)
<class'int'>
>>> type(two)
<class'tuple'>

The difference is that in line one the variable 1 is of type int wherea isn line two the variable type is of type tuple. Why? This is because in line two, there is a comma after the number 2. This is an indication to the Python interpreter to create a tuple and not an int.

4. Dictionary
Some important points regarding dictionary are as follows:

  • It has “unordered” key-value pairs.
  • A dictionary is a container, which contains other objects. So, you cannot “access” the items of a dictionary through an “index” but you can access the items of a dictionary through its “keys”.
  • So, a dictionary has keys and for each key there is a value, which can be an object of any type. Just like in maths you have a function definition as y = f(x), dictionaries are something similar. You can think of x as keys and y as their corresponding values. Just like in a mathematical function for one key there can be only one value (Though the reverse need not be true, that is, for the same value there can be different keys or to put it differently, different keys may have the same values).
  • A dictionary is a ‘mapping’ between x and y or between the keys and its values.
  • Dictionaries are enclosed by curly braces { } and values can be assigned and accessed using square braces [].
  • The keys in a dictionary must be unique. The key value pairs are separated by a colon, that is, :.
  • The keys of a dictionary must be of an “immutable data type” such as strings, numbers, or tuples.

The following script shows how an item in a dictionary can be accessed through its “key”:

# ---ON IDLE---  
>>> myD = {"A": "Apple", "B": "Baby", "C": "Cat", "D": "Dog"}
>>> myD["C"]
'Cat'

5. Sets
A set contains an unordered collection of immutable and unique objects. Sets, unlike lists or tuples, cannot have multiple occurrences of the same element. There are three important words in this definition:

  1. A set is a collection.
  2. It is unordered.
  3. The elements must be unique.

You can think of a set in Python as a dictionary with no value, that is, a dictionary which only has keys.
Remember, a dictionary uses curly braces and has a pair comprising a key and a value.
hink of a set as a data type, which has no values but has only keys.
Just like a dictionary, the items of a set can be enclosed in curly brackets, but they must all be unique.
In case there are duplicate items, the Python interpreter will simply keep only one instance of the duplicate items.

# ---ON IDLE---  
>>> myS ={4,4,4,5,6,6,0,0,0,1,1}
>>> myS
{0, 1, 4, 5, 6}
>>>

1. Creating a set from a string:-
A set can be created from a string as follows (Note all duplicate items will be removed):

# ---ON IDLE---  
>>> myS =set('cat dog bat rat') #The string will be converted to set of characters
>>> myS
{'a', 't', ' ', 'o', 'd', 'b', 'c', 'g', 'r'}

2. Creating set from a list.
Note, a set cannot have lists as its items but it can “convert” a list into a set. Again, only unique items will be retained and all duplicates will be dropped.

# ---ON IDLE---  
>>> myL = ['cat', 'cat', 'dog', 'rat'] # Lists can contain duplicates
>>> myS = set(myL)
>>> myS
{'rat', 'dog', 'cat'}
>>> myS

However, if you try to create a set with a list as an item, it will throw an error as shown:

# ---ON IDLE---  
>>> myS = set([1,2,[3,4]])
Traceback (most recent call last):
  File "<pyshell#31>", line 1, in<module>
    myS = set([1,2,[3,4]])
TypeError: unhashable type: 'list'
>>>

Adding items to a set
Since a set is mutable, items can be added to it. This can be done by two methods:

  1. add()
  2. update()

If you need to add a single item to a set, you can use the add() method. However, to add multiple elements to a set, use the update() method. The update() method can take strings, list tuples or even other sets as its argument. In all cases, duplicates are ignored. This is shown in the following code:

# ---ON IDLE---  
>>> myS = {1,2,3}
>>> myS.add(4)
>>> myS
{1, 2, 3, 4}
>>> myS.update([1,2,3,4,5], {6,7,8})
>>> myS
{1, 2, 3, 4, 5, 6, 7, 8}

2.2.5 None
The following points regarding the data type “None” are relevant:

  • “None” is used to define a “null” value or “no value”.
  • None does not mean either “0” or “an empty container”, such as an empty string.
  • None also does not mean False.
  • None does not mean undefined.
  • None data type is a real data type, which occupies space in memory and can be assigned to a variable just as any other data type.
  • In many programming languages, such as Java/ C++, the keyword “null” is used rather than None.

The following example code explains the concepts:

# ---ON IDLE---  
>>> myNone = None  # Now myNone is of None type
>>>print(myNone)
None
>>> type(myNone)    # Output shows that myNone is indeed of None type
<class'NoneType'>
>>> bool(myNone)    # A None variable evaluates to bool False
False

You can use None as any other type in Python. For instance, you can create a list of None as follows:

# ---ON IDLE---  
>>> myL = [None] * 10 # Will create a list of 10 None
>>> myL
[None, None, None, None, None, None, None, None, None, None]

2.3 Mutable versus immutable

Python represents all its data as objects. Some objects, such as lists and dictionaries are mutable.
This means, you can change their content without changing their identity.
However, there are objects, such as integers, floats, strings and tuples, which are immutable.
An immutable object means you cannot change its contents without changing its identity.
If you try to assign new content to an immutable object, then a new object is created rather than contents being modified.
You can confirm this by using the function id(obj_name) to get an object’s ID.
The following example code explains the concepts:

# ---ON IDLE---  
>>> s1 = 'abcd'  # s1 is a string
>>> id(s1)
36219904
>>> s2 = 'abcdef'  # s2 is another but different string
>>> id(s2)
36219872
>>> s1[1]    # You get the character at index 0 ie 2nd character of s1
'b'
>>> s1[1] = 'x' # Error since cannot change characters of a string
Traceback (most recent call last):
  File "<pyshell#15>", line 1, in<module>
    s1[1] = 'x'# Error since cannot change characters of a string
TypeError: 'str' object does not support item assignment
>>>

Now take a list, which is mutable in Python:

# ---ON IDLE---  
>>> myL = ['a', 'b', 'c']
>>> id(myL)
36207192
>>> myL[0] = 'x'  # Change item at index 0 ie 1st item of list
>>> myL
['x', 'b', 'c']     # Items in list can be changed-> mutable
>>> id(myL)         # Changing items in a list doesn’t change its id
36207192

A common confusion regarding immutable objects can arise when you modify immutable objects as follows:

# ---ON IDLE---  
>>> s1 = "hello"
>>> s2 = s1
>>> id(s1)
35824800
>>> id(s2)
35824800
>>> s1 = s1 + "world" # the original string "hello" has not been mutated
# But a new string "helloworld" has been created
# s1 no more points to "hello". It now points to "helloworld"
# s1 is now a new tag as clear from its id()
>>> s1
'helloworld'
>>> id(s1)
4056032
>>> id(s2)  #But s2 continues to point to same string "hello"
35824800

2.4 Type casting (Also called type conversion) in Python
(Code is in small fragments, so it is better toread from the book)

2.4.2 Implicit type conversion in boolean context
Some important Boolean type conversions are as follows:

  • Empty string is mapped to False.
  • Non-empty string is mapped to True.
  • Integer 0 is mapped to False.
  • Every non-zero integer (Including negatives) is mapped to True.

Moreover, the following are considered False in Python:-

  • None
  • False
  • Zero of any numeric type, for instance, Numeric integer →0, Numeric float→ 0.0, Numeric complex→ 0j.
  • Any empty sequence, for instance, Empty string→ '', Empty tuple→ (), Empty list→ [].
  • Any empty mapping, for instance, Empty dictionary→ {}.

The following shows how type conversion to bool works in Python:

# ---ON IDLE---  
>>>print(bool(5))  # Positive convert converts to bool True
True
>>>print(bool(-6)) # Negative ints also convert to bool True
True
>>>print(bool(0))  # int 0 (Zero) converts to bool False
False
>>>print(bool("Any string"))   # Any  Non-empty string converts to bool True
True
>>>print(bool(''))# Empty string (Not even blank space) converts to bool False
False
>>>print(bool('  '))# String ‘  ‘ has 2 white spaces so not empty so bool True
True
>>>print(bool(0.0)) ))#float 0.0 converts to bool False.
False
>>>print(bool([])) #Empty list is False
False
>>>print(bool([1,2,3])) #List with items is bool True
True

2.5 Input to a Python program
(The scripts in the beginning of this topic are small and need detailed explanation and hec not covered here. Some of the scripts in the later part of this topic along with accompanying explanation are given below)

Note, the input method in Python 3.x always returns a string. If you want to use it as an integer, convert it using the int method.

# ---ON IDLE---  
>>>myInput = input("Say something..")
Say something..Hello world     
# User input is Hello world and -> (assigned) to myInput  by input() function
>>>print (myInput)
Hello world
>>> type(myInput)
<class'str'>.

Suppose your program is expecting an integer as input, then what do you do? Well, you have to cast your input from a string to an integer. Casting is explained later, but for the present the return value of int(some_object) will convert that object into an integer if it can be converted and if not, it will throw an error. For instance, if you give it a string of numbers it will be converted to an integer, but if you give it a string of letters, there will be an error. Similarly, if you want a float number then you have to explicitly cast this input to a float using float(some_object). Again, if the object passed is a string or some other object, such as an integer which can be converted to a float, it will be done, else there will be an error. This is shown on IDLE as follows:

# ---ON IDLE---  
>>> myInput = input('give integer-> ')
give integer->123
>>> myInput        # Note myInput is a string
'123'
>>> myInt = int(myInput)  # If you want input to be a string, you need to cast it
>>> type(myInt)
<class'int'>
>>> myInput = input('give float-> ')
give float->222.333
>>> float(myInput)  # Again you need to cast the myInput to a float
222.333
>>> myInput = input('give another integer-> ')
give another integer-> abc
>>> int(myInput)# Since user input ‘abc’, which cannot be cast to int -> error
Traceback (most recent call last):
  File "<pyshell#34>", line 1, in<module>
    int(myInput)
ValueError: invalid literal for int() with base 10: 'abc'

2.6.1 Accessing the attributes and methods of a module
A module may have attributes and methods. Both are used with the dot that is, ‘.’ operator.
Variables defined inside a module are called attributes of the module. They are accessed by using the dot operator (.)
For instance, Python has a built-in module called string . This string module has many attributes. One of them is digits.
The following output on IDLE shows this:

# ---ON IDLE---  
>>>import string
>>> string
<module 'string'from'C:\\Python34\\lib\\string.py'>
>>> string.digits
'0123456789'

Similarly, pi is an attribute of the math module and can be accessed as shown:

# ---ON IDLE---  
>>>import math
>>> math.pi
3.141592653589793

2.6.2 Function defined inside modules are called methods of the module.
Just like attributes, you can also have functions inside a module, but these functions are called methods of the module. They can also be accessed using the dot operator.
One important difference between attributes and methods is that a method name is always accompanied by brackets. Further, the brackets may or may not contain a list of attributes.
For instance, Python has a math.factorial(x) method where x has to be a non-negative integer (If a negative number of a float is given, there will be an error). Here, x is the parameter given to the factorial method of the math module.
This is shown as follows:

# ---ON IDLE---  
>>> math.factorial(10)
3628800

2.7.1 Using string function len(str) on a “literal string”

# ---ON IDLE---  
>>>len("Hello World!")    
12

Applying a “Method” to a “String Literal”.
As an example, take a string literal, say “CAPITAL” and apply the lower() method to it:-

# ---ON IDLE---  
>>>"CAPITAL".lower()
'capital'

2.7.2 Applying a function and a method to a variable, which refers to a string
Take a variable, say myString and refer it to a string “HELLO WORLD!”. Now apply function len() to it and also a method lower() to it. This is shown as follows:

# ---ON IDLE---  
>>>myString = "HELLO WORLD!"# Create a variable to refer to a string
>>> len(myString)    # Use function len() and pass it a string object as argument
12
>>> myString.lower()   # Use lower() method of string object using dot(.) operator
'hello world!'

2.7.3 Python strings are "immutable"
This means, strings cannot be changed after they are created.
The concept of mutable and immutable is very important in Python and explained in detail later. For now, it is sufficient to understand that strings once created cannot be changed. For instance, suppose you have a string variable myStr pointing to ‘cat’ and you want to change the ‘cat’ to ‘rat’, the following script shows what happens:

# ---ON IDLE---  
>>> myStr = 'cat'# Create a string literal ‘cat’ and assign it to variable myStr
>>> myStr[0]
'c'
>>> myStr[0] = 'r'
Traceback (most recent call last):
  File "<pyshell#4>", line 1, in<module>
    myStr[0] = 'r'
TypeError: 'str' object does not support item assignment
>>>

2.7.5 The '+' operator can concatenate two strings
This has already been explained earlier. The point is that when two strings are the two operands with a + operator between them, the Python interpreter is smart enough to understand that the operation to be performed is ‘concatenation’ and not ‘arithmetic addition. This is shown as follows:

# ---ON IDLE---  
>>> int1 = 5
>>> int2 = 10
>>> int1+ int2  # int1 and int2 are of type int so integer addition is performed
15
>>> str1 = '5'
>>> str2 = '10'
>>> str1 + str2  # str1 and str2 are of type str so concatenation performed
'510'

2.7.6 The str(object) function converts objects to strings
The function str(object) takes as its argument an object and returns its string representation. The variable name ‘object’ is what is called an argument to a function.
It is what is given or passed to a function. The return value of a function is what the function returns when it finishes its execution.
A function of the type str(object), can be thought of as a factory, which takes in an object and gives back its string equivalent. If you take an integer object say myInt = 1234 and use the str function, then the output is as follows:

# ---ON IDLE---  
>>> myInt = 1234
>>> myStr = str(myInt)        
>>> myStr          
'1234'
>>> str(1234)      #Applying str() function on a numeric “literal”
'1234'

2.7.7 Single quotes within double quotes
Use of single quotes and double quotes can be helpful in certain circumstances. For instance, suppose you want to print a statement— She said “hi!”. You can do this as follows:

# ---ON IDLE---  
>>>print('She said "hi!"')
She said "hi!"

2.7.8 Indexing of strings
In Python, a string is an “ordered collection” of characters. This means, not only are the individual characters important, but their order is also important.
In Python, the individual characters forming a string can be accessed by an “index”. In Python, the index is a “numeric offset” in a square bracket.
Numeric offset means the position from the beginning of the string, just as in in C++, it starts from 0. However, there is an important difference from C++. In C++ there is no negative index whereas in Python there is also negative index and -1 indicates the last character in the string. You can think of negative index as counting backwards, that is, in reverse from the last character in the string.
Indexing applies both to literal strings as well as string variables. For instance, if you have a string variable say myStr, which points to a string ‘Hello World!’, then its first character can be accessed as follows:

# ---ON IDLE---  
>>>myStr = 'Hello World!'
>>> myStr[0]
'H'
>>>'Hello World'[0]
'H'

2.8 Binary Literals in Python
Note that binary literals in Python are represented by appending 0b or 0B. Thus, if you want to write the number 7 in binary, which is 111, then you need to write it as 0b111 or 0B111. Further, there is an inbuilt Python function bin(), which converts decimal numbers to Binary. Similarly, there is an inbuilt function int() which can be used to convert a binary number to decimal format.

# ---ON IDLE---  
>>>print(0b111) #Converts binary 111 to decimal 7
7
>>>print(bin(7)) # Converts decimal 7 to binary 111
0b111
>>>print(0b121) #Error since only digits 0 and 1 allowed
SyntaxError: invalid syntax

But note that in Python, the numbers are internally stored in their decimal representation. This is clear from the following:

# ---ON IDLE---  
>>> x = 0b1100
>>>print(x) #x stores the decimal equivalent of 0b1100
12

2.9 The Zen of Python on Jupyter
Python as a programming language has a “Zen”. You can see the “Zen of Python” by typing import this on Python as follows:

In [1]:
import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!